#include "cudaSetup.h"

#include <stdio.h>

#include <cuda_gl_interop.h>
#include <cuda_runtime_api.h>

#include "cuda_lighting.h"

cudaGraphicsResource *depth;
cudaGraphicsResource *normal;
cudaGraphicsResource *miscA;
cudaGraphicsResource *miscB;
cudaGraphicsResource *diffuse;
cudaGraphicsResource *out;
cudaGraphicsResource *edge;

// Returns true if an error occurred
bool errorOccurred(cudaError_t errors) {
	switch(errors) {
		case cudaSuccess:
			return false;
			break;
		case cudaErrorInvalidDevice:
			printf("Invalid device: make sure to set the cuda device before calling.\n");
			return true;
			break;
		case cudaErrorInvalidValue:
			printf("Invalid value passed to CUDA.\n");
			return true;
			break;
		case cudaErrorInvalidResourceHandle:
			printf("Invalid resource handle passed to CUDA.\n");
			return true;
			break;
		case cudaErrorUnknown:
			printf("Unknown error.\n");
			return true;
			break;
		default:
			printf("Untested error.\n");
			return true;
			break;
	}
}


cuda_resource_container getCudaResources(bool aFrame) {
	cuda_resource_container c;
	c.depth = depth;
	c.diffuse = diffuse;
	if(aFrame) {
		c.misc = miscA;
		c.miscB = miscB;
	}
	else {
		c.misc = miscB;
		c.miscB = miscA;
	}
	c.normal = normal;
	c.out = out;
	c.edge = edge;

	return c;
}

void unregisterCUDATextures () {
	cudaError_t error = cudaSuccess;

	if(depth)
		error = cudaGraphicsUnregisterResource(depth);
	errorOccurred(error);
	depth = NULL;

	if(miscA)
		error = cudaGraphicsUnregisterResource(miscA);
	errorOccurred(error);
	miscA = NULL;

	if(miscB)
		error = cudaGraphicsUnregisterResource(miscB);
	errorOccurred(error);
	miscB = NULL;

	if(normal)
		error = cudaGraphicsUnregisterResource(normal);
	errorOccurred(error);
	normal = NULL;

	if(diffuse)
		error = cudaGraphicsUnregisterResource(diffuse);
	errorOccurred(error);
	diffuse = NULL;

	if(out)
		error = cudaGraphicsUnregisterResource(out);
	errorOccurred(error);
	out = NULL;

	if(edge)
		error = cudaGraphicsUnregisterResource(edge);
	errorOccurred(error);
	out = NULL;


	setCUDAInit(false);
}

bool registerCUDATextures(GLuint depthTexture, GLuint normalTexture, GLuint miscTextureA, GLuint miscTextureB, GLuint diffuseTexture, GLuint outTexture, GLuint edgeTexture) {
	if(depthTexture == 0 || normalTexture == 0 || miscTextureA == 0 || miscTextureB ==0 || diffuseTexture == 0 || outTexture == 0) {
		printf("The input textures need to be created before registerCudaTextures() is called.\n");
		return false;
	}

	cudaError_t error;
	error = cudaGraphicsGLRegisterImage(&depth, depthTexture, GL_TEXTURE_2D, cudaGraphicsRegisterFlagsReadOnly);
	if(errorOccurred(error))
		return false;

	error = cudaGraphicsGLRegisterImage(&normal, normalTexture, GL_TEXTURE_2D, cudaGraphicsRegisterFlagsReadOnly);
	if(errorOccurred(error))
		return false;

	error = cudaGraphicsGLRegisterImage(&miscA, miscTextureA, GL_TEXTURE_2D, cudaGraphicsRegisterFlagsReadOnly);
	if(errorOccurred(error))
		return false;

	error = cudaGraphicsGLRegisterImage(&miscB, miscTextureB, GL_TEXTURE_2D, cudaGraphicsRegisterFlagsReadOnly);
	if(errorOccurred(error))
		return false;

	error = cudaGraphicsGLRegisterImage(&diffuse, diffuseTexture, GL_TEXTURE_2D, cudaGraphicsRegisterFlagsReadOnly);
	if(errorOccurred(error))
		return false;

	error = cudaGraphicsGLRegisterImage(&out, outTexture, GL_TEXTURE_2D, cudaGraphicsRegisterFlagsWriteDiscard);
	if(errorOccurred(error))
		return false;

	error = cudaGraphicsGLRegisterImage(&edge, edgeTexture, GL_TEXTURE_2D, cudaGraphicsRegisterFlagsReadOnly);
	if(errorOccurred(error))
		return false;

	/*
	cudaArray *a;
	cudaGraphicsMapResources(1, &out);
	cudaGraphicsSubResourceGetMappedArray(&a, out, 0, 0);

	cudaChannelFormatDesc d;
	cudaGetChannelDesc(&d, a);

	cudaGraphicsUnmapResources(1, &out);

	printf("testing?");
	*/

	setCUDAInit(true);
	return true;
}